"use client"; import { useRouter } from "next/navigation"; import { useEffect, useMemo, useState } from "react"; import { hasQuestionAnswerValue, QuestionAnswersProvider, useQuestionAnswers, } from "@/components/questions/question-answer-storage"; import QuestionExitNavigationButton from "@/components/questions/question-exit-navigation-button"; import QuestionRenderer from "@/components/questions/question-renderer"; import QuestionSectionFlow from "@/components/questions/question-section-flow"; import TestIntroPage from "@/components/questions/test-intro-page"; import TestQuestionsFlow, { type TestQuestion, } from "@/components/questions/test-questions-flow"; import { DotsLoader } from "@/components/ui/button"; import NavigationButton from "@/components/ui/navigation-button"; import StickyHeader from "@/components/ui/sticky-header"; import { PageBackground } from "@/components/utils/page-background"; import { cattellFallbackQuestions } from "@/data/cattell-fallback"; import { glasserFallbackQuestions } from "@/data/glasser-fallback"; import { getQuestionListItemBySlug, isQuestionListItemVisibleForProfile, isQuestionRequiredForProfile, isQuestionVisibleForProfile, type QuestionField, } from "@/data/question-data"; import type { MarriageGender } from "@/hooks/marriage/types"; import { useCattellQuestionsQuery, useSubmitCattellAssessmentMutation, } from "@/hooks/marriage/use-cattell"; import { useGlasserQuestionsQuery, useSubmitGlasserAssessmentMutation, } from "@/hooks/marriage/use-glasser"; import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; import { defaultLocale, type Locale } from "@/translations/config"; import { useI18n } from "@/translations/provider"; import AnswerPaceSheet from "./answer-pace-sheet"; type QuestionDetailClientProps = { closeLabel: string; continueLabel: string; description: string; informationLabel: string; itemSlug: string; locale?: Locale; questionsListHref: string; title: string; }; type StoredQuestionField = { label?: string; value?: unknown; type?: string; key?: string; }; type StoredAnswers = { fields?: StoredQuestionField[]; }; function getQuestionStorageKey(slug: string) { return `marriage:sections:${slug}:answers`; } function parseStoredAge(value: unknown) { if (typeof value === "number" && Number.isFinite(value)) { return value; } if (typeof value === "string") { const trimmedValue = value.trim(); if (!trimmedValue) { return null; } const numericAge = Number(trimmedValue); if (Number.isFinite(numericAge)) { return numericAge; } const dateOfBirth = new Date(trimmedValue); if (Number.isNaN(dateOfBirth.getTime())) { return null; } const today = new Date(); let age = today.getFullYear() - dateOfBirth.getFullYear(); const hasBirthdayPassed = today.getMonth() > dateOfBirth.getMonth() || (today.getMonth() === dateOfBirth.getMonth() && today.getDate() >= dateOfBirth.getDate()); if (!hasBirthdayPassed) { age -= 1; } return age >= 0 ? age : null; } return null; } function getStoredAge() { try { const rawValue = window.localStorage.getItem( getQuestionStorageKey("personal_info"), ); if (!rawValue) { return null; } const storedAnswers = JSON.parse(rawValue) as StoredAnswers; const ageField = storedAnswers.fields?.find( (field) => field.type === "number" || field.label === "Age" || field.label === "سن" || (typeof (field as any).key === "string" && ((field as any).key.endsWith("_age") || (field as any).key.endsWith("_sn"))), ); if (ageField) { return parseStoredAge(ageField.value); } const dateOfBirthField = storedAnswers.fields?.find( (field) => field.type === "date" || field.label === "Date of Birth" || field.label === "تاریخ تولد" || (typeof (field as any).key === "string" && ((field as any).key.endsWith("_date_of_birth") || (field as any).key.endsWith("_tarykh_twld"))), ); return parseStoredAge(dateOfBirthField?.value); } catch { return null; } } function QuestionFlowWrapper({ visibleQuestions, itemSlug, dobQuestion, dobQuestionIndex, continueLabel, questionsListHref, }: { visibleQuestions: QuestionField[]; itemSlug: string; dobQuestion?: QuestionField; dobQuestionIndex?: number; requiredQuestionsCount: number; continueLabel: string; questionsListHref: string; }) { const { getAnswerValue } = useQuestionAnswers(); const dynamicQuestions = useMemo(() => { return visibleQuestions.filter((question) => { if (question.logic?.dependsOn) { const { title, values } = question.logic.dependsOn; const dependentQuestionIndex = visibleQuestions.findIndex( (q) => q.title === title, ); if (dependentQuestionIndex !== -1) { const dependentQuestion = visibleQuestions[dependentQuestionIndex]; const answer = getAnswerValue( dependentQuestion, dependentQuestionIndex, ); return values.includes(String(answer)); } return false; } return true; }); }, [visibleQuestions, getAnswerValue]); const requiredCount = useMemo( () => dynamicQuestions.filter((q) => q.required).length, [dynamicQuestions], ); return ( question.required ? [] : [index], )} questions={dynamicQuestions} > {dynamicQuestions.map((question, index) => { const originalIndex = visibleQuestions.indexOf(question); const answer = getAnswerValue(question, originalIndex); const hasAnswer = hasQuestionAnswerValue(answer ?? null); let isAnswered = hasAnswer; if (hasAnswer) { const isEmailQuestion = question.title.toLowerCase().includes("email") || question.title.includes("ایمیل"); if (isEmailQuestion) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; isAnswered = emailRegex.test(String(answer).trim()); } else if (question.type === "birthplace") { const strVal = String(answer); const parts = strVal.split(",").map((p) => p.trim()); isAnswered = parts.length >= 2 && parts[0].length > 0 && parts[1].length > 0; } } return (
); })}
); } export default function QuestionDetailClient({ closeLabel, continueLabel, description, informationLabel, itemSlug, locale = defaultLocale, questionsListHref, title, }: QuestionDetailClientProps) { const router = useRouter(); const { dictionary: t } = useI18n(); const [isTestStarted, setIsTestStarted] = useState(false); const { data: profile, isLoading: isProfileLoading } = useMarriageProfileQuery(); const profileGender = profile?.gender; const age = getStoredAge(); const item = getQuestionListItemBySlug(itemSlug, locale); const isCattellSlug = itemSlug === "personality_test"; const isGlasserSlug = itemSlug === "glasser_5_needs_test"; const cattellQuery = useCattellQuestionsQuery(locale, { enabled: isCattellSlug && isTestStarted, retry: 0, }); const submitCattellMutation = useSubmitCattellAssessmentMutation(); const glasserQuery = useGlasserQuestionsQuery(locale, { enabled: isGlasserSlug && isTestStarted, retry: 0, }); const submitGlasserMutation = useSubmitGlasserAssessmentMutation(); const profileContext = useMemo( () => ({ age, gender: profileGender as MarriageGender | null | undefined, }), [age, profileGender], ); const cattellTestQuestions: TestQuestion[] = useMemo(() => { const questionsList = cattellQuery.data?.questions && cattellQuery.data.questions.length > 0 ? cattellQuery.data.questions : cattellFallbackQuestions; return questionsList.map((q) => { const rawOptions = q.options && q.options.length > 0 ? q.options : locale === "fa" ? ["بله", "به اندازه کافی واضح نیست", "نه"] : ["Yes", "Not clear enough", "No"]; const mappedOptions = rawOptions.map((optText, idx) => ({ label: optText, value: idx === 0 ? "A" : idx === 1 ? "B" : "C", })); return { id: q.question_number, text: q.text, options: mappedOptions, }; }); }, [cattellQuery.data]); const glasserTestQuestions: TestQuestion[] = useMemo(() => { const questionsList = glasserQuery.data?.questions && glasserQuery.data.questions.length > 0 ? glasserQuery.data.questions : glasserFallbackQuestions; const defaultGlasserOptions = [ { label: locale === "fa" ? "خیلی کم (۱)" : "Very Low (1)", value: 1 }, { label: locale === "fa" ? "کم (۲)" : "Low (2)", value: 2 }, { label: locale === "fa" ? "متوسط (۳)" : "Moderate (3)", value: 3 }, { label: locale === "fa" ? "زیاد (۴)" : "High (4)", value: 4 }, { label: locale === "fa" ? "خیلی زیاد (۵)" : "Very High (5)", value: 5 }, ]; return questionsList.map((q) => ({ id: q.question_number, text: q.text, info: "factor" in q ? (q.factor as string) : "factor_code" in q ? (q.factor_code as string) : undefined, options: defaultGlasserOptions, })); }, [glasserQuery.data, locale]); const visibleQuestions = useMemo(() => { if (!item) { return []; } const hasDobQuestion = item.questions.some( (q) => q.title === "Date of Birth" || q.title === "تاریخ تولد", ); return item.questions .filter((question) => { if ( hasDobQuestion && (question.title === "Age" || question.title === "سن") ) { return false; } return isQuestionVisibleForProfile(question, profileContext); }) .map((question) => ({ ...question, required: isQuestionRequiredForProfile(question, profileContext), })); }, [item, profileContext]); const requiredQuestionsCount = useMemo( () => visibleQuestions.filter((q) => q.required).length, [visibleQuestions], ); useEffect(() => { if (isProfileLoading) { return; } if (!item || isQuestionListItemVisibleForProfile(item, profileContext)) { return; } router.replace(questionsListHref); }, [isProfileLoading, item, profileContext, questionsListHref, router]); if (isProfileLoading && item) { return ( <>
); } else if ( !item || !isQuestionListItemVisibleForProfile(item, profileContext) ) { return null; } if (item && item.questions.length === 0) { if (isTestStarted) { const isQuestionsLoading = isCattellSlug ? cattellQuery.isLoading || cattellQuery.isFetching : isGlasserSlug ? glasserQuery.isLoading || glasserQuery.isFetching : false; if (isQuestionsLoading) { return ( <>
); } const activeTestQuestions = isCattellSlug ? cattellTestQuestions : isGlasserSlug ? glasserTestQuestions : []; if (activeTestQuestions.length === 0) { const isError = isCattellSlug ? cattellQuery.isError : isGlasserSlug ? glasserQuery.isError : false; const refetch = isCattellSlug ? cattellQuery.refetch : glasserQuery.refetch; return ( <>

{isError ? locale === "fa" ? "خطا در دریافت سوالات از سرور. لطفاً از اتصال اینترنت یا ورود به حساب کاربری اطمینان حاصل کنید." : "Failed to load questions from server. Please check your connection or login status." : locale === "fa" ? "سوالاتی برای این آزمون یافت نشد." : "No questions found for this test."}

); } const handleTestFinish = async ( answers: Record, ) => { if (isCattellSlug) { const responses = Object.entries(answers).map(([qNum, option]) => ({ question_number: Number(qNum), option: String(option), })); try { await submitCattellMutation.mutateAsync({ responses }); } catch { // Ignore if already submitted or API returned error } try { window.localStorage.setItem( getQuestionStorageKey(item.slug), JSON.stringify({ completed: true }), ); } catch {} } else if (isGlasserSlug) { const responses = Object.entries(answers).map(([qNum, score]) => ({ question_number: Number(qNum), score: Number(score), })); try { await submitGlasserMutation.mutateAsync({ responses }); } catch { // Ignore if already submitted } try { window.localStorage.setItem( getQuestionStorageKey(item.slug), JSON.stringify({ completed: true }), ); } catch {} } await new Promise((resolve) => setTimeout(resolve, 1200)); }; return ( setIsTestStarted(false)} onFinish={handleTestFinish} /> ); } const bulletKey = item.slug === "glasser_5_needs_test" ? "glasser" : "personality"; const bullets = t.questions.testIntroBullets[bulletKey]; return ( <>

{item.title}

{ setIsTestStarted(true); }} />
); } const dobQuestion = visibleQuestions.find( (question) => question.title === "Date of Birth", ); const dobQuestionIndex = visibleQuestions.findIndex( (question) => question.title === "Date of Birth", ); return ( <>

{item.title}

); }